/*
 * Cyclometer.cpp
 *
 * The controller of the system that interfaces
 * the state machine with the input/output interfaces
 * of the system.
 *
 * Author: Zachary Masiello
 * Author: Peter-John Rowe
 */

#include "Cyclometer.h"

#include "Transition.h"
#include "IState.h"
#include "InitDataState.h"
#include "DistModeState.h"
#include "UnitSetState.h"
#include "SetTireRunState.h"
#include "SetTireConfState.h"
#include "TimeModeState.h"
#include "SpeedModeState.h"
#include "InitData_UnitSet_Transition.h"
#include "UnitSet_InitData_Transition.h"
#include "UnitSet_UnitSet_1_Transition.h"
#include "UnitSet_UnitSet_2_Transition.h"
#include "UnitSet_SetTireConf_Transition.h"
#include "SetTireConf_InitData_Transition.h"
#include "SetTireConf_Speed_Transition.h"
#include "SetTireConf_SetTireConf_Transition.h"
#include "Speed_Speed_1_Transition.h"
#include "SetTireRun_SetTireRun_Transition.h"
#include "SetTireRun_Dist_Transition.h"
#include "SetTireRun_InitData_Transition.h"
#include "Speed_Speed_2_Transition.h"
#include "Speed_Speed_3_Transition.h"
#include "Speed_Dist_Transition.h"
#include "Speed_InitData_Transition.h"
#include "Dist_Time_Transition.h"
#include "Dist_SetTireRun_Transition.h"
#include "Dist_Dist_2_Transition.h"
#include "Dist_Dist_1_Transition.h"
#include "Time_Time_1_Transition.h"
#include "Time_Time_2_Transition.h"
#include "Time_Time_3_Transition.h"
#include "Time_Speed_Transition.h"
#include "Time_InitData_Transition.h"

#include <sys/neutrino.h>
#include <hw/inout.h>
#include <sys/mman.h>
#include <stdint.h>
#include <pthread.h>
#include <unistd.h>
#include <cstdlib>
#include <iostream>

// Method to test the state machione
void Cyclometer::testStateMachine() {
	cout << "Start the cyclometer!" << endl;
	run();
	cout << "End Test" << endl;
}

// Initiates the state machine
void Cyclometer::run()
{
	bool runTest = true;

	while(runTest)
	{
		if (table->getCurrentState()->getID() == INITIAL_DATA_STATE_ID)
		{
			accept(new UnitSetEvent());
		}

	}
}

// Constructor
Cyclometer::Cyclometer(Calculator *newCalculator, LEDDisplay *newLEDDisplay)
{
	calculator = newCalculator;
	ledDisplay = newLEDDisplay;
	createStateTable();
}

// Destructor
Cyclometer::~Cyclometer()
{
	delete table;
}

// Creates the state table
void Cyclometer::createStateTable()
{
	// Initialize State Table
	table = new StateTable();

	// Initialize states for state machine
	IState* distModeState = new DistModeState(this);
	IState* initDataState = new InitDataState(this);
	IState* setTireConState = new SetTireConfState(this);
	IState* setTireRunState = new SetTireRunState(this);
	IState* speedModeState = new SpeedModeState(this);
	IState* timeModeState = new TimeModeState(this);
	IState* unitSetState = new UnitSetState();

	// Assign States to state state table
	assignState(distModeState);
	assignState(setTireConState);
	assignState(setTireRunState);
	assignState(speedModeState);
	assignState(timeModeState);
	assignState(unitSetState);

	// Assign state to state table and set default Initial State
	setDefaultInitialState(initDataState);

	// Initialize transitions for State Machine
	Transition* initData_unitSet_T = new InitData_UnitSet_Transition(this,unitSetState,UNIT_SET_ID);

	Transition* unitSet_initData_T = new UnitSet_InitData_Transition(initDataState,ALL_BUTTON_ID);
	Transition* unitSet_unitSet_T1 = new UnitSet_UnitSet_1_Transition(this,unitSetState,MODE_BUTTON_ID);
	Transition* unitSet_unitSet_T2 = new UnitSet_UnitSet_2_Transition(this,unitSetState,MODE_BUTTON_ID);
	Transition* unitSet_setTireConf_T = new UnitSet_SetTireConf_Transition(setTireConState,SET_BUTTON_ID);

	Transition* setTireConf_initData_T = new SetTireConf_InitData_Transition(initDataState,ALL_BUTTON_ID);
	Transition* setTireConf_speed_T = new SetTireConf_Speed_Transition(speedModeState,SET_BUTTON_ID);
	Transition* setTireConf_setTireConf_T = new SetTireConf_SetTireConf_Transition(this,setTireConState,MODE_BUTTON_ID);

	Transition* setTireRun_setTireRun_T = new SetTireRun_SetTireRun_Transition(this,setTireRunState,MODE_BUTTON_ID);
	Transition* setTireRun_dist_T = new SetTireRun_Dist_Transition(distModeState,SET_BUTTON_ID);
	Transition* setTireRun_initData_T = new SetTireRun_InitData_Transition(initDataState,ALL_BUTTON_ID);

	Transition* speed_speed_T1 = new Speed_Speed_1_Transition(this,speedModeState,SET_BUTTON_ID);
	Transition* speed_speed_T2 = new Speed_Speed_2_Transition(this,speedModeState,SSMODE_BUTTON_ID);
	Transition* speed_speed_T3 = new Speed_Speed_3_Transition(this,speedModeState,SS_BUTTON_ID);
	Transition* speed_dist_T = new Speed_Dist_Transition(distModeState,MODE_BUTTON_ID);
	Transition* speed_initData_T = new Speed_Dist_Transition(initDataState,ALL_BUTTON_ID);

	Transition* dist_time_T = new Dist_Time_Transition(timeModeState,MODE_BUTTON_ID);
	Transition* dist_setTireRun_T = new Dist_SetTireRun_Transition(setTireRunState,SET_BUTTON_ID);
	Transition* dist_dist_T1 = new Dist_Dist_1_Transition(this,distModeState,SS_BUTTON_ID);
	Transition* dist_dist_T2 = new Dist_Dist_2_Transition(this,distModeState,SSMODE_BUTTON_ID);

	Transition* time_time_T1 = new Time_Time_1_Transition(this,timeModeState,SET_BUTTON_ID);
	Transition* time_time_T2 = new Time_Time_2_Transition(this,timeModeState,SS_BUTTON_ID);
	Transition* time_time_T3 = new Time_Time_3_Transition(this,timeModeState,SSMODE_BUTTON_ID);
	Transition* time_initData_T = new Time_InitData_Transition(initDataState,ALL_BUTTON_ID);
	Transition* time_speed_T = new Time_Speed_Transition(speedModeState,MODE_BUTTON_ID);

	// Assign Transitions to state table
	assignTransition(initDataState,initData_unitSet_T);
	assignTransition(unitSetState,unitSet_initData_T);
	assignTransition(unitSetState,unitSet_unitSet_T1);
	assignTransition(unitSetState,unitSet_unitSet_T2);
	assignTransition(unitSetState,unitSet_setTireConf_T);
	assignTransition(setTireConState,setTireConf_initData_T);
	assignTransition(setTireConState,setTireConf_speed_T);
	assignTransition(setTireConState,setTireConf_setTireConf_T);
	assignTransition(setTireRunState,setTireRun_setTireRun_T);
	assignTransition(speedModeState,speed_dist_T);
	assignTransition(speedModeState,speed_initData_T);
	assignTransition(speedModeState,speed_speed_T1);
	assignTransition(speedModeState,speed_speed_T2);
	assignTransition(speedModeState,speed_speed_T3);
	assignTransition(distModeState,dist_time_T);
	assignTransition(distModeState,dist_setTireRun_T);
	assignTransition(distModeState,dist_dist_T1);
	assignTransition(distModeState,dist_dist_T2);
	assignTransition(setTireRunState,setTireRun_dist_T);
	assignTransition(setTireRunState,setTireRun_initData_T);
	assignTransition(timeModeState,time_time_T1);
	assignTransition(timeModeState,time_time_T2);
	assignTransition(timeModeState,time_time_T3);
	assignTransition(timeModeState,time_speed_T);
	assignTransition(timeModeState,time_initData_T);
}

// Assigns the state to the state table
void Cyclometer::assignState(IState* state)
{
	table->assignState(state);
}

// Assigns the transition to the state table based on the state that would
// previous state of the transition
void Cyclometer::assignTransition(IState* state,Transition* tran)
{
	table->assignTransition(state,tran);
}

// Sets the initial state in the state machine
void Cyclometer::setDefaultInitialState(IState* initial)
{
	table->assignState(initial);
	table->setCurrentState(initial);
}

// Accepts an event and passes it to the state table
void Cyclometer::accept(IEvent* event)
{
	table->accept(event);
}

// Triggers the calculator to do a full reset
void Cyclometer::fullDataReset()
{
	calculator->fullReset();
}

// Initial action to be done
void Cyclometer::initialAction()
{
	fullDataReset();
}

// Gets the distance calculated by the calculator,
// and trigger the LED Display to display it
void Cyclometer::displayDistance()
{
	double distance = calculator->getDistance();
	if(distance < 1)
	{
		distance = distance * 10;
		ledDisplay->setDisplay(12,12,11,(int)distance);
	}
	else
	{
		//get decimal place
		int dec = distance * 10;
		dec = dec % 10;
		//get ones place
		int ones = (int)distance % 10;
		if(ones == 0)
		{
			ones = 11;
		}
		else
		{
			ones = ones * 10;
		}
		//get tens place
		int tens = (int)distance % 100;
		tens = tens / 10;
		//get hundreds place
		int hundreds = (int)distance % 1000;
		hundreds = hundreds / 100;
		if(hundreds == 0)
		{
			hundreds = 12;
			if(tens == 0)
			{
				tens = 12;
			}
		}
		ledDisplay->setDisplay(hundreds, tens, ones, dec);
	}
}

// Triggers the calculator to set the wheel circumference
// and triggers the LED Display to display the circumference
void Cyclometer::setWheelCircum()
{
	calculator->setCircumference(calculator->getCircumference());
	getAndDisplayCircumference();
}

// Increments the tire size by first getting the current tire size
// from the calculator and then triggering the LED Display to display it
// and then triggers the calculator to increment it and then finally
// triggers the LED Display to trigger display the new tire circumference
void Cyclometer::incrementTireSize()
{
	getAndDisplayCircumference();
	calculator->incrementTmpTireCircum();
	calculator->setTmpAsPerCircum();
	getAndDisplayCircumference();
}

// Gets the current and average speed from the calculator and
// and displays them
void Cyclometer::displaySpeed()
{
	double currentSpeed = calculator->getCurrentSpeed();
	double averageSpeed = calculator->getAverageSpeed();

	int cTens = 12;
	int cOnes = 12;
	int aTens = 12;
	int aOnes = 12;

	if(currentSpeed < 10)
	{
		cOnes = currentSpeed * 10;
		cOnes = cOnes % 10;
		cTens = (int)currentSpeed % 10;
		if(cTens == 0)
		{
			cTens = 11;
		}
		else
		{
			cTens = cTens * 10;
		}
	}
	else
	{
		//get Ones Place
		cOnes = (int)currentSpeed % 10;
		cOnes = cOnes;
		//get tens place
		cTens = (int)currentSpeed % 100;
		cTens = cTens / 10;
	}

	if(averageSpeed < 10)
	{
		aOnes = averageSpeed * 10;
		aOnes = aOnes % 10;
		aTens = (int)averageSpeed % 10;
		if(aTens == 0)
		{
			aTens = 11;
		}
		else
		{
			aTens = aTens * 10;
		}
	}
	else
	{
		//get Ones Place
		aOnes = (int)averageSpeed % 10;
		//aOnes = aOnes * 10;
		//get tens place
		aTens = (int)averageSpeed % 100;
		aTens = aTens / 10;
	}
	ledDisplay->setDisplay(cTens, cOnes, aTens, aOnes);
}

// Gets the elapsed time from the calculator
// and then triggers the LED display to display it
void Cyclometer::displayTime()
{
	int elapsedTime = calculator->getElapsedTime();
	int minutes = elapsedTime / 60;
	int seconds = elapsedTime % 60;
	int secOnes = seconds % 10;
	int secTens = seconds % 100;
	secTens = secTens / 10;

	int minTens = minutes % 100;
	minTens = minTens / 10;
	int minOnes = minutes % 10;
	if(minOnes == 0)
	{
		minOnes = 11;
	}
	else
	{
		minOnes = minOnes * 10;
	}
	ledDisplay->setDisplay(minTens, minOnes, secTens, secOnes);
}

// Triggers the calculator to set the measurement units and
// then trigger the LED Display to display the new units that were set
void Cyclometer::setUnit()
{
	calculator->setUnits();
	ledDisplay->setDisplay(12,12,12,calculator->getUnits());

}

// Gets the circumference from the calculator and
// triggers the LED display to display
void Cyclometer::getAndDisplayCircumference()
{
	int circum = calculator->getCircumference();
	int hundreds = circum % 1000;
	hundreds = hundreds / 100;
	int tens = circum % 100;
	tens = tens / 10;
	int ones = circum % 10;
	ledDisplay->setDisplay(12, hundreds, tens, ones);
}

// Evaluates the measurement units to see if its in
// kilometers
bool Cyclometer::isUnitK()
{
	return calculator->getUnits() == KILOMETERS;
}

// Evaluates the measurement units to see if its in
// miles
bool Cyclometer::isUnitM()
{
	return calculator->getUnits() == MILES;
}

// Triggers the calculator to change the calculation mode
void Cyclometer::changeCalcMode()
{
	calculator->changeCalcMode();
}

// Triggers the calculator to perform a data reset
void Cyclometer::resetTripValues()
{
	calculator->dataReset();
}

// Triggers the calculator to start/stop calculation
void Cyclometer::startStopCalc()
{
	calculator->changeCalcOnOff();
}

// Evaluates the calculation mode
bool Cyclometer::isCalcModeM()
{
	return calculator->getCalcMode() == 'm';
}

// Gets the state table
StateTable* Cyclometer::getStateTable()
{
	return table;
}
